package hierarchies;

import hips.Hierarchy;
import hips.gui.ComponentLocation;
import hips.gui.WaitingFrame;
import hips.region.NewRegionEvent;
import hips.region.NewRegionListener;
import hips.gui.images.Image3;
import hips.gui.images.Image4;
import hips.gui.parameters.ParameterFrame;
import hips.gui.parameters.ParameterListener;
import hips.images.ImagePartitionable;
import hips.pixel.PixelValue;
import hips.region.Region;
import ij.gui.StackWindow;
import java.awt.Color;
import java.awt.Container;
import java.awt.Scrollbar;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Date;
import java.util.LinkedList;

public class Main implements ParameterListener, WindowListener, MouseListener, MouseMotionListener, NewRegionListener, AdjustmentListener, MouseWheelListener {

    static long ELAPSED = 50;
    Hierarchy h;
    Frame frame;
    long time;

    public hips.images.Image output;
    private ImagePartitionable f;
    public Region highlighted;
    private boolean highlightEnabled;
    private boolean visualizeLastCreation;
    private boolean listening;
    LinkedList<Long> times;

    private int lastPixel;

    public Main(ImagePartitionable f) {
        this.f = f;

        WaitingFrame wait = new WaitingFrame();
        wait.setVisible(true);
        output = hips.gui.images.Image.getOutputImage(f);
        h = new Hierarchy(f);
        wait.dispose();
        
        lastPixel = 0;

        listening = false;

        times = new LinkedList<Long>();
        

        frame = new MainFrame(this);

        highlighted = null;
        highlightEnabled = true;
        frame.setHighlightEnabled(highlightEnabled);

        frame.setPixels(f.getSize());
        frame.setPartitions(0);
    }

    private void setPartitionInfo(int level) {
        int slice = output.getImagePlus().getSlice() - 1;
        frame.setLocalRange(h.getPartition(level).getAlpha().getValue(slice));
        frame.setGlobalRange(h.getPartition(level).getOmega().getValue(slice));
        frame.setCindex(h.getPartition(level).getCindex());
        frame.setRegions(h.getPartition(level).getRegionSize());
        frame.setTime(times.get(level));
    }

    public void setHighlightStatus(boolean enabled) {
        highlightEnabled = enabled;
        if (!enabled && highlighted != null) {
            output.paintRegion(highlighted, f.toRGB(f.getMeanValue(highlighted)));
            highlighted = null;
        }
    }

    public void visualize(int level) {
        highlighted = null;
        h.setPartition(level);
        setPartitionInfo(level);
    }

    private void listen(){
        if (! listening){
            output.getImagePlus().getCanvas().addMouseListener(this);
            output.getImagePlus().getCanvas().addMouseMotionListener(this);
            output.getImagePlus().getWindow().addWindowListener(this);
            output.getImagePlus().getWindow().addMouseWheelListener(this);
            listening = true;
        }
    }

    private void dontListen(){
        if (listening){
            output.getImagePlus().getCanvas().removeMouseListener(this);
            output.getImagePlus().getCanvas().removeMouseMotionListener(this);
            output.getImagePlus().getWindow().removeWindowListener(this);
            output.getImagePlus().getWindow().removeMouseWheelListener(this);
            listening = false;
        }
    }

    public void createPartition() {
        visualizeLastCreation = false;
        ParameterFrame p = new ParameterFrame(f);
        p.addParameterListener(this);
        frame.setEnabled(false);
        dontListen();
    }

    public void createPartitionAndVisualize() {
        visualizeLastCreation = true;
        ParameterFrame p = new ParameterFrame(f);
        p.addParameterListener(this);
        frame.setEnabled(false);
        dontListen();
    }

    public void adjustmentValueChanged(AdjustmentEvent e) {
        showLocalAndGlobal(lastPixel);
        showColor(lastPixel);
    }

    private void showColor(int px) {
        PixelValue pixel = f.getPixelValue(px);
        Region r = h.getRegion(px);
        int slice = output.getImagePlus().getSlice() - 1;
        if (output instanceof Image3) {
            frame.setPixelValue(Image3.getStringFrom(pixel));
            frame.setRegionColor(Image3.getStringFrom(f.getMeanValue(r)));
        } else if (output instanceof Image4) {
            frame.setPixelValue(Image4.getStringFrom(pixel, slice));
            frame.setRegionColor(Image4.getStringFrom(f.getMeanValue(r), slice));
        } else {
            frame.setPixelValue(pixel.getString(slice));
            frame.setRegionColor(f.getMeanValue(r).getString(slice));
        }
    }

    private void showLocalAndGlobal(int px) {
        int slice = output.getImagePlus().getSlice() - 1;
        if (output instanceof Image3) {
            frame.setLocalRange(Image3.getStringFrom(h.getPartition(h.getLevelId(px)).getAlpha()));
            frame.setGlobalRange(Image3.getStringFrom(h.getPartition(h.getLevelId(px)).getOmega()));
        } else if (output instanceof Image4) {
            frame.setLocalRange(Image4.getStringFrom(h.getPartition(h.getLevelId(px)).getAlpha(), slice));
            frame.setGlobalRange(Image4.getStringFrom(h.getPartition(h.getLevelId(px)).getOmega(), slice));
        } else {
            frame.setLocalRange(h.getPartition(h.getLevelId(px)).getAlpha().getString(slice));
            frame.setGlobalRange(h.getPartition(h.getLevelId(px)).getOmega().getString(slice));
        }
    }

    public void close() {
        if (output.getImagePlus() != null) {
            output.getImagePlus().getCanvas().removeMouseListener(this);
            output.getImagePlus().getCanvas().removeMouseMotionListener(this);
            output.getImagePlus().getWindow().removeWindowListener(this);
            output.getImagePlus().getWindow().removeMouseWheelListener(this);
            output.getImagePlus().close();
        }
    }

    public void mouseClicked(MouseEvent e) {
        if (e.getButton() != MouseEvent.BUTTON1) {
            return;
        }
        int x = output.getImagePlus().getCanvas().offScreenX(e.getX());
        int y = output.getImagePlus().getCanvas().offScreenY(e.getY());
        int pos = output.getWidth() * y + x;

        //h.colourComponent(frame.highlighted);
//        h.setOutput(main.output);
        if (!e.isShiftDown()) {
            h.increaseLevel(pos);
        } else {
            h.decreaseLevel(pos);
        }
        highlighted = h.getRegion(pos);
        if (highlightEnabled) {
            output.paintRegion(highlighted, output.newPixelValue(Color.YELLOW.getRGB()));
        }
        frame.setLevel(h.getLevelId(pos));
        frame.setRegionId(h.getRegion(pos).getLabel());
        frame.setRegionSize(h.getRegionsSize());
    }

    public void mouseMoved(MouseEvent e) {
        int x = output.getImagePlus().getCanvas().offScreenX(e.getX());
        int y = output.getImagePlus().getCanvas().offScreenY(e.getY());
        int pos = output.getWidth() * y + x;
        Region r = h.getRegion(pos);
        int slice = output.getImagePlus().getSlice() - 1;
        if (highlighted == null && highlightEnabled) {
            highlighted = r;
            output.paintRegion(r, output.newPixelValue(Color.YELLOW.getRGB()));
        } else if (highlighted != null && !r.sameAs(highlighted) && highlightEnabled) {
            output.paintRegion(highlighted, f.toRGB(f.getMeanValue(highlighted)));
            output.paintRegion(r, output.newPixelValue(Color.YELLOW.getRGB()));
            highlighted = r;
        }
        if ((new Date()).getTime() - time > ELAPSED) {
            lastPixel = pos;
            frame.setPixel(x, y);
            showColor(lastPixel);
            frame.setLevel(h.getLevelId(pos));
            frame.setRegionId(h.getRegion(pos).getLabel());
            frame.setRegionSize(r.getSize());
            time = System.currentTimeMillis();
        }
    }
 
    public void windowClosed(WindowEvent e) {
        if (e.getWindow() == output.getImagePlus().getWindow()) {
            //info.dispose();
        }
    }

    public void windowDeactivated(WindowEvent e) {
        if (highlighted != null) {
//            main.output.colourRegion(main.highlighted);
            highlighted = null;
        }
    }

    public void mouseWheelMoved(MouseWheelEvent e) {
        showLocalAndGlobal(lastPixel);
        showColor(lastPixel);
    }

    

    public void newRegionCreated(NewRegionEvent e) {
        Region r = e.getRegion();
        output.paintRegion(r, f.toRGB(f.getMeanValue(r)));
    }

    public void mousePressed(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseDragged(MouseEvent e) {
    }

    public void windowOpened(WindowEvent e) {
    }

    public void windowClosing(WindowEvent e) {
    }

    public void windowIconified(WindowEvent e) {
    }

    public void windowDeiconified(WindowEvent e) {
    }

    public void windowActivated(WindowEvent e) {
    }

    public void ParametersOk(PixelValue alpha, PixelValue omega, float cindex) {
        Integer place = h.getPlaceForPartition(alpha, omega, cindex);
        if (place == null) {
            //errorLabel.setText("partition couldn't be created");
            frame.setEnabled(true);
            return;
        }
        if (highlighted != null) {
            output.paintRegion(highlighted, f.toRGB(f.getMeanValue(highlighted)));
            highlighted = null;
        }

        long t = System.currentTimeMillis();

        if (output.getImagePlus().getWindow() == null) {
            output.getImagePlus().show();
            ComponentLocation.setLocationCenter(0.75f, 0.5f, output.getImagePlus().getWindow());
            if (output.getImagePlus().getStackSize() > 1) {
                StackWindow stack = (StackWindow) output.getImagePlus().getWindow();
                Container c = (Container) stack.getComponent(1);
                Scrollbar s = (Scrollbar) c.getComponent(1);
                s.addAdjustmentListener(this);
            }
        }

        if (visualizeLastCreation || h.getSize() == 0) {
            output.clear();
            h.addPartition(alpha, omega, cindex, this);
        } else {
            h.addPartition(alpha, omega, cindex);
            //errorLabel.setText("ok"); // PONER AKI UNA VENTANA CON BARRA DE PROGRESO
        }

        t = System.currentTimeMillis() - t;
        times.add(place, t);
        frame.setPartitions(h.getSize());
        if (visualizeLastCreation) {
            h.setPartition(place);
            setPartitionInfo(place);
        }
        if (h.getSize() == 1) {
            setPartitionInfo(0);
        } else if (h.getSize() > 1) {
            frame.allowVisualize(h.getSize());
        }
        frame.setEnabled(true);
        listen();
    }

    public void ParametersCancelled() {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
